NUM Client
A TypeScript/JavaScript client for the NUM protocol.
See the full Specification at the NUM Protocol Website and the Explainer for more information.
The NUM protocol supports a range of modules that add rich functionality in an extensible way.
The NUM URI Scheme
The NUM protocol uses the familiar URL format for its URIs and allows modules to interpret data in a variety of ways.
The data stored in a NUM Record is converted to JSON String format that can be parsed into JSON objects for straightforward incorporation into TypeScript and JavaScript programs. Here are some example NUM URIs with module 1
- the Contacts module. The default module is 0
(zero) if no module is specified, which has no module schema.
num://numexample.com:1
num://jo.smith@numexample.com:1
num://jo.smith@numexample.com:1/work
num://jo.smith@numexample.com:1/personal
num://jo.smith@numexample.com:1/hobbies
num://numexample.com:1/support
num://numexample.com:1/support/website
num://numexample.com:1/support/delivery
num://numexample.com:1/enquiries
num://numexample.com:1/sales
As you can see from the examples above, data can be associated with domains and email addresses, and can be organised hierarchically if desired. In future, the protocol will support more than just domains and email addresses.
Additional modules can be referenced in the same way as ports
in other URIs:
num://numexample.com:2
for the Registrant module.num://numexample.com:3
for the Images module.num://numexample.com:4
for the Custodians module.num://numexample.com:5
for the Payments module.num://numexample.com:6
for the Regulatory module.num://numexample.com:7
for the Public Key module.num://numexample.com:8
for the Intellectual Property module.num://numexample.com:9
for the Terms module.num://numexample.com:10
for the Bugs module.num://numexample.com:nn
for your own module?
Adding Support for the NUM Protocol
Install using:
npm install -s num-client
TypeScript Examples
Importing the Client
Use this import to make the client available for use:
import {
createClient,
createDnsClient,
parseNumUri,
CallbackHandler,
DoHResolver,
Location
} from 'num-client';
The Simplest Usage
The programming interface is very simple:
const lookup = async () => {
const numUri = parseNumUri('num.uk:1');
const client = createClient();
const ctx = client.createContext(numUri);
const result = await client.retrieveNumRecord(ctx);
console.log(result);
}
Reusing the NUMClient
The same NUMClient
can be reused for multiple lookups, as in this example:
const lookup = async () => {
const numUri1 = parseNumUri('num.uk:1');
const numUri2 = parseNumUri('numexample.com:1');
const client = createClient();
const ctx1 = client.createContext(numUri1);
const ctx2 = client.createContext(numUri2);
const result1 = client.retrieveNumRecord(ctx1);
const result2 = client.retrieveNumRecord(ctx2);
const result = await Promise.all([result1, result2]);
console.log(result[0]);
console.log(result[1]);
}
Overriding the Default DoH Endpoint
By default the NUMClient
uses the Cloudflare and Google DoH resolvers, although it can be changed if required by providing a DoHResolver
to a service that supports the JSON API for DNS over HTTPS (DoH).:
const lookup = async () => {
const DEFAULT_RESOLVERS = [
new DoHResolver('Cloudflare', 'https://cloudflare-dns.com/dns-query'),
new DoHResolver('Quad9', 'https://dns10.quad9.net:5053/dns-query'),
];
const client = createClient(DEFAULT_RESOLVERS);
};
Providing User Variable Values
Some modules can be provided with User Variable values to customise the output, as in this example:
const lookup = async () => {
const numUri = parseNumUri('num.uk:1');
const client = createClient();
const ctx = client.createContext(numUri);
ctx.setUserVariable('_L', 'en');
ctx.setUserVariable('_C', 'gb');
const result = await client.retrieveNumRecord(ctx);
console.log(result)
}
Using a CallbackHandler
Lookups can take several seconds, so you can provide a CallbackHandler
rather than await
ing the results:
const lookup = async () => {
const numUri = parseNumUri('num.uk:1');
const client = createClient();
const ctx = client.createContext(numUri);
const handler: CallbackHandler = {
setLocation: (l: NumLocation): void => {
console.log(l);
},
setResult: (r: string): void => {
console.log(r);
},
setErrorCode: (e: string): void => {
console.log(e);
}
};
client.retrieveNumRecord(ctx, handler).then((_r) => {
}, (err) => console.error(err));
}
JavaScript Examples
The Simplest Usage
This example shows the minimal requirements for using the NUM Client:
const num = require('num-client');
num.lookup('num.uk:1').then((result) => console.log(result));
Full Usage
This example shows how to use all features of the client, including
- overriding the DoH resolver,
- reusing the
NUMClient
- setting user variables
- using a callback handler
const num = require('num-client');
function lookup(uri1, uri2) {
const numUri1 = num.parseNumUri(uri1);
const numUri2 = num.parseNumUri(uri2);
const DEFAULT_RESOLVERS = [
new DoHResolver('Cloudflare', 'https://cloudflare-dns.com/dns-query'),
new DoHResolver('Quad9', 'https://dns10.quad9.net:5053/dns-query'),
];
const client = num.createClient(DEFAULT_RESOLVERS);
const ctx1 = client.createContext(numUri1);
const ctx2 = client.createContext(numUri2);
ctx1.setUserVariable('_L', 'en');
ctx1.setUserVariable('_C', 'gb');
ctx2.setUserVariable('_L', 'en');
ctx2.setUserVariable('_C', 'us');
const handler = {
setLocation: (l) => {
console.log(l);
},
setResult: (r) => {
console.log(r);
},
setErrorCode: (e) => {
console.log(JSON.stringify(e));
}
};
const result1 = client.retrieveNumRecord(ctx1, handler);
const result2 = client.retrieveNumRecord(ctx2, handler);
return Promise.all([result1, result2]);
}
lookup('num.uk:1', 'numexample.com:1').then((result) => {
});
HTML and JavaScript Example
The Simplest Usage
This simple example can be modified as necessary by following the previous examples above.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>NUM Protocol Example</title>
</head>
<body>
<script src="../dist/bundle.js"></script>
<h1>NUM Protocol Example</h1>
<div>
NUM URI = <input type="text" value="num.uk:1" id='urivalue' onchange="reloadRecord()">
<input type="button" value="Reload" onclick="reloadRecord()">
</div>
<div style="border: 1px solid blue;width: fit-content;">
<pre id='num'></pre>
</div>
<script>
const CUSTOM_RESOLVERS = [
new NumClient.DoHResolver('Cloudflare', 'https://cloudflare-dns.com/dns-query')
];
const client = NumClient.createClient(CUSTOM_RESOLVERS);
function lookup(uri) {
const numUri = NumClient.parseNumUri(uri);
const ctx = client.createContext(numUri);
return client.retrieveNumRecord(ctx);
}
function reloadRecord() {
const uri = document.getElementById('urivalue').value;
lookup(uri).then((result) => {
const pretty = JSON.stringify(JSON.parse(result), null, 1);
document.getElementById('num').innerHTML = pretty;
});
}
window.addEventListener('load', function () {
reloadRecord();
});
</script>
</body>
</html>